package com.tianma315.wx.service.impl;


import com.tianma315.core.common.exception.MessageException;
import com.tianma315.core.wx.domain.WxJsapiTicket;
import com.tianma315.core.wx.domain.WxScanConfig;
import com.tianma315.core.wx.domain.WxToken;
import com.tianma315.wx.service.WxService;
import com.tianma315.wx.utils.WeiXinUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;


//                   _ooOoo_
//                  o8888888o
//                  88" . "88
//                  (| -_- |)
//                  O\  =  /O
//               ____/`---'\____
//             .'  \\|     |//  `.
//            /  \\|||  :  |||//  \
//           /  _||||| -:- |||||-  \
//           |   | \\\  -  /// |   |
//           | \_|  ''\---/''  |   |
//           \  .-\__  `-`  ___/-. /
//         ___`. .'  /--.--\  `. . __
//      ."" '<  `.___\_<|>_/___.'  >'"".
//     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
//     \  \ `-.   \_ __\ /__ _/   .-` /  /
//======`-.____`-.___\_____/___.-`____.-'======
//                   `=---='
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//         佛祖保佑       永无BUG

/**
 * asdsa
 * Created by zcm on 2018/5/14.
 */
@Service
public class WxServiceImpl extends RestService implements WxService {
    private Logger logger = LoggerFactory.getLogger(getClass());

    public Logger getLogger() {
        return logger;
    }

    private static final String KEY_TOKEN = "token";
    private static final String KEY_TICKET = "ticket";
    private static final Map<String, Object> wxCache = new HashMap(); //保存微信token的缓存  暂时保存在内存中
    private static final String URL_GET_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    private static final String URL_GET_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";

    @Value("${app.wx.app-id}")
    private String appId;
    @Value("${app.wx.app-secret}")
    private String appSecret;

    @Override
    public WxToken getWxToken() {
        WxToken token = (WxToken) wxCache.get(KEY_TOKEN);
        String url = URL_GET_TOKEN.replace("APPID", appId).replace("APPSECRET", appSecret);
        if (token == null || tokenExpires(token)) {
            getLogger().info("更新微信token {}", url);
            token = getRestTemplate().getForObject(url, WxToken.class);
            wxCache.put(KEY_TOKEN, token);
        }
        getLogger().info("微信token {}", token);
        return token;
    }

    @Override
    public WxJsapiTicket getWxJsapiTicket() {
        WxToken token = getWxToken();
        if (token.isError())
            throw new MessageException("微信token获取失败");
        WxJsapiTicket ticket = (WxJsapiTicket) wxCache.get(KEY_TICKET);
        String url = URL_GET_TICKET.replace("ACCESS_TOKEN", token.getAccess_token());
        if (ticket == null || ticketExpires(ticket)) {
            getLogger().info("更新微信ticket {}", url);
            ticket = getRestTemplate().getForObject(url, WxJsapiTicket.class);
            wxCache.put(KEY_TICKET, ticket);
        }
        getLogger().info("微信ticket {}", ticket);
        return ticket;
    }

    @Override
    public WxScanConfig getWxScanConfig(String currentUrl) {
        if (StringUtils.isBlank(currentUrl)) {
            throw new MessageException("url 不能为空");
        }
        currentUrl = currentUrl.split("#")[0];
        WxScanConfig config = new WxScanConfig();
        config.setAppId(appId);
        config.setNonceStr(generateRandomStr());
        config.setUrl(currentUrl);
        config.setSignature(generateSignature(config.getNonceStr(), config.getTimestamp(), config.getUrl()));
        return config;
    }

    /**
     * @return
     */
    private String generateRandomStr() {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuffer buffer = new StringBuffer();
        int i = 0;
        while (i < 32) {
            int index = (int) (Math.random() * str.length());
            buffer.append(str.charAt(index));
            i++;
        }
        return buffer.toString();
    }


    /**
     * @param nonceStr
     * @param timestamp
     * @param url
     * @return
     */
    private String generateSignature(String nonceStr, long timestamp, String url) {
        WxJsapiTicket ticket = getWxJsapiTicket();
        if (ticket.isError())
            throw new MessageException("微信ticket获取失败");
//
        Map<String, Object> map = new TreeMap<>();
        map.put("noncestr", nonceStr);
        map.put("jsapi_ticket", ticket.getTicket());
        map.put("timestamp", timestamp);
        map.put("url", url);
        Set<String> keySet = map.keySet();
        Iterator<String> it = keySet.iterator();
        StringBuffer buffer = new StringBuffer();
        while (it.hasNext()) {
            String key = it.next();
            Object val = map.get(key);
            buffer.append(key);
            buffer.append("=");
            buffer.append(val);
            if (it.hasNext()) {
                buffer.append("&");
            }
        }
        return WeiXinUtil.SHA1(buffer.toString());
    }


    /**
     * 验证token是否有效 返回true表示token过期
     *
     * @param token
     * @return
     */
    private boolean ticketExpires(WxJsapiTicket token) {
        //token提前三个小时过期
        long current_date = new Date().getTime() - (3 * 60 * 60 * 1000);
        long invalidate_date = token.getCreate_date() + (token.getExpires_in() * 1000);
        if (invalidate_date > current_date) {
            return false;
        }
        return true;
    }

    /**
     * 验证ticket是否有效 返回true表示ticket过期
     *
     * @param token
     * @return
     */
    private boolean tokenExpires(WxToken token) {
        //token提前三个小时过期
        long current_date = new Date().getTime() - (3 * 60 * 60 * 1000);
        long invalidate_date = token.getCreate_date() + (token.getExpires_in() * 1000);
        if (invalidate_date > current_date) {
            return false;
        }
        return true;
    }

}
